home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
lisp
/
isearch.jl
< prev
next >
Wrap
Lisp/Scheme
|
1995-03-09
|
9KB
|
283 lines
;;;; isearch.jl -- Emacs style incremental search
;;; Copyright (C) 1994 John Harper <jsh@ukc.ac.uk>
;;; This file is part of Jade.
;;; Jade is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;; Jade is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;; You should have received a copy of the GNU General Public License
;;; along with Jade; see the file COPYING. If not, write to
;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
(provide 'isearch)
;;; Bugs:
;;; Doesn't un-wrap itself when backtracking
(defvar case-fold-search t
"Buffer-local variable, when non-nil case of characters is ignored when
searching.")
(make-variable-buffer-local 'case-fold-search)
(defvar isearch-last-match nil
"The last regexp successfully (i.e. entered with RET) found by isearch.")
;;; Wrappers for some regexp functions to trap errors
(defun isearch-find-next-regexp (pos)
(setq isearch-re-error nil)
(error-protect
(find-next-regexp (car (car isearch-trace)) pos nil case-fold-search)
(regexp-error
(setq isearch-re-error t)
'regexp-error)))
(defun isearch-find-prev-regexp (pos)
(setq isearch-re-error nil)
(error-protect
(find-prev-regexp (car (car isearch-trace)) pos nil case-fold-search)
(regexp-error
(setq isearch-re-error t)
'regexp-error)))
(defun isearch-looking-at ()
(setq isearch-re-error nil)
(error-protect
(looking-at (car (car isearch-trace)) nil nil case-fold-search)
(regexp-error
(setq isearch-re-error t)
'regexp-error)))
;;; I-search stack handling. The variable `isearch-trace' holds a stack of
;;; all strings and match positions in the current search. It looks
;;; something like,
;;; ((SEARCH-REGEXP [MATCH-POS]... )... )
;;; But although the current string will be on the stack, the current match
;;; position won't -- the cursor position is used to show this.
;; Pops the top match and moves the cursor to it.
(defun isearch-pop-match ()
(let
((item (car isearch-trace)))
(if (cdr item)
(progn
(goto-char (nth 1 item))
(rplaca isearch-trace (cons (car item) (nthcdr 2 item))))
(goto-char isearch-initial-pos)))
(setq isearch-failing nil))
;; Pops the top string, then moves to the top match of the next string.
(defun isearch-pop-string ()
(when (cdr isearch-trace)
(setq isearch-trace (cdr isearch-trace))
(isearch-pop-match)))
;; Pushes a match at POS onto the top string
(defun isearch-push-match (pos)
(let
((item (car isearch-trace)))
(rplaca isearch-trace (cons (car item) (cons pos (cdr item))))))
;; Pushes the current position, pushes the STRING onto the top of the
;; stack, then searches for it
(defun isearch-push-string (string)
(isearch-push-match (cursor-pos))
(setq isearch-trace (cons (cons string nil) isearch-trace))
(unless (isearch-looking-at)
(let
((next (if isearch-forwards
(isearch-find-next-regexp (next-char))
(isearch-find-prev-regexp (prev-char)))))
(cond
((posp next)
(goto-char next)
(setq isearch-failing nil))
((null next)
(setq isearch-failing t)
(beep))))))
;; Keymap
(defvar isearch-keymap (make-keylist))
(bind-keys isearch-keymap
"Ctrl-s" 'isearch-next-forward
"Ctrl-r" 'isearch-next-backward
"Ctrl-g" 'isearch-cancel
"Ctrl-q" '(progn (setq next-keymap-path nil) (isearch-title))
"Ctrl-w" 'isearch-yank-word
"Ctrl-y" 'isearch-yank-line
"RET" 'isearch-accept
"ESC" 'isearch-accept
"BS" 'isearch-rubout)
;; Display the status message, has to be done after each command
(defun isearch-title ()
(let
((msg (concat (if isearch-failing "failing ")
(if isearch-wrapped "wrapped ")
"I-search: "
(car (car isearch-trace))
(if isearch-re-error " *Invalid/incomplete regexp*"))))
(aset msg 0 (char-upcase (aref msg 0)))
(message msg)))
;; Accept our current position
(defun isearch-accept ()
(interactive)
(set-mark auto-mark isearch-initial-pos isearch-buffer)
(message "Set auto-mark.")
(setq isearch-last-match (car (car isearch-trace)))
(throw 'isearch (cursor-pos)))
;; Cancel the search or if search is failing backtrack to the last match
(defun isearch-cancel ()
(interactive)
(if (not (isearch-looking-at))
(progn
(while (and (not (isearch-looking-at)) (cdr isearch-trace))
(isearch-rubout))
(isearch-title))
(goto-char isearch-initial-pos)
(throw 'isearch nil)))
;; Copy the rest of the current word to the search string
(defun isearch-yank-word ()
(interactive)
(when (isearch-looking-at)
(isearch-push-string (concat (car (car isearch-trace))
(copy-area (match-end)
(forward-word 1 (match-end))))))
(isearch-title))
;; Copy the rest of the line to the search string
(defun isearch-yank-line ()
(interactive)
(when (isearch-looking-at)
(isearch-push-string (concat (car (car isearch-trace))
(copy-area (match-end) (line-end)))))
(isearch-title))
;; Backup one match/string
(defun isearch-rubout ()
(interactive)
(cond
((cdr (car isearch-trace))
(isearch-pop-match))
((cdr isearch-trace)
(isearch-pop-string))
(t
(error "Beginning of I-search")))
(isearch-title))
;; Add the typed character to the search string
(defun isearch-unbound-key-fun ()
(let
((str (current-event-string)))
(if (/= (length str) 1)
(isearch-accept)
(isearch-push-string (concat (car (car isearch-trace)) str))))
(isearch-title))
(defun isearch (isearch-forwards)
(let
((isearch-trace (cons (cons "" nil) nil))
(isearch-initial-pos (cursor-pos))
(isearch-buffer (current-buffer))
isearch-failing
isearch-wrapped
isearch-re-error
(esc-means-meta nil) ; want to bind to ESC
(old-kp keymap-path))
(setq keymap-path '(isearch-keymap))
(add-hook 'unbound-key-hook 'isearch-unbound-key-fun)
(unwind-protect
(catch 'isearch
(isearch-title)
(recursive-edit))
(with-buffer isearch-buffer
(remove-hook 'unbound-key-hook 'isearch-unbound-key-fun)
(setq keymap-path old-kp)))))
;;;###autoload
(defun isearch-forward ()
"Enters the incremental search mode. You are then able to type letters to add
to the regexp being searched for. Special commands are,\n
`Ctrl-s' Search forwards for another occurrence
`Ctrl-r' Search backwards
`Ctrl-g' If the search is failing, backtrack to the first non-failing
match, else, cancel the search leaving the cursor at its
original position.
`Ctrl-w' Copy the rest of the current word into the search string
`Ctrl-y' Copy the rest of the line to the search string
`RET' Exit isearch leaving the cursor at its current position
`BS' Retrace your movements one step"
(interactive)
(isearch t))
;;;###autoload
(defun isearch-backward ()
"Similar to `isearch-forward' except the first searching is done in the other
direction."
(interactive)
(isearch nil))
(defun isearch-next-forward ()
(interactive)
(if (and (equal (car (car isearch-trace)) "") isearch-last-match)
(progn
(isearch-push-string isearch-last-match)
(isearch-title))
(let
((next (if (and isearch-failing isearch-forwards)
(progn
(setq isearch-wrapped t)
(buffer-start))
(next-char))))
(setq next (isearch-find-next-regexp next))
(cond
((posp next)
(isearch-push-match (cursor-pos))
(setq isearch-failing nil)
(goto-char next))
((null next)
(setq isearch-failing t)
(beep)))
(setq isearch-forwards t)
(isearch-title))))
(defun isearch-next-backward ()
(interactive)
(if (and (equal (car (car isearch-trace)) "") isearch-last-match)
(progn
(isearch-push-string isearch-last-match)
(isearch-title))
(let
((next (if (and isearch-failing (not isearch-forwards))
(progn
(setq isearch-wrapped t)
(buffer-end))
(prev-char))))
(setq next (isearch-find-prev-regexp next))
(cond
((posp next)
(isearch-push-match (cursor-pos))
(setq isearch-failing nil)
(goto-char next))
((null next)
(setq isearch-failing t)
(beep)))
(setq isearch-forwards nil)
(isearch-title))))